#pragma once

//template implement
namespace matsu
{
#if _IPHONE_
#pragma mark vector2 impl
#endif

	template<typename T>
	Vector2<T>::Vector2() : x(0), y(0) {}
	template<typename T>
	Vector2<T>::Vector2(T x, T y) : x(x), y(y) { }

	template<typename T>
	Vector2<T>::Vector2(T *ptr) : x(ptr[0]), y(ptr[1]) {}

	template<typename T>
	Vector2<T>::Vector2(const T *ptr) : x(ptr[0]), y(ptr[1]) {}

	template<typename T>
	T* Vector2<T>::pointer() { return &x; }

	template<typename T>
	bool Vector2<T>::operator==(const Vector2<T> &o) const
	{
		return (x == o.x && y == o.y);
	}
	template<typename T>
	bool Vector2<T>::operator!=(const Vector2<T> &o) const
	{
		return !(*this == o);
	}
	template<typename T>
	Vector2<T> Vector2<T>::operator+(const Vector2<T> &o) const
	{
		return Vector2<T>(x + o.x, y + o.y);
	}
	template<typename T>
	Vector2<T> Vector2<T>::operator-(const Vector2<T> &o) const
	{
		return Vector2<T>(x - o.x, y - o.y);
	}
	template<typename T>
	Vector2<T> Vector2<T>::operator*(T t) const
	{
		return Vector2<T>(x * t, y * t);
	}
	template<typename T>
	Vector2<T> Vector2<T>::operator/(T t) const
	{
		//if(t == 0)
		//	t = 1;
		return Vector2<T>(x / t, y / t);
	}
	template<typename T>
	Vector2<T>& Vector2<T>::operator+=(const Vector2<T> &o)
	{
		x += o.x;
		y += o.y;
		return *this;
	}
	template<typename T>
	Vector2<T>& Vector2<T>::operator-=(const Vector2<T> &o)
	{
		x -= o.x;
		y -= o.y;
		return *this;
	}
	template<typename T>
	Vector2<T>& Vector2<T>::operator*=(T t)
	{
		x *= t;
		y *= t;
		return *this;
	}
	template<typename T>
	Vector2<T>& Vector2<T>::operator/=(T t)
	{
		x /= t;
		y /= t;
		return *this;
	}
	template<typename T>
	Vector2<T> Vector2<T>::normal() const
	{
		Vector2<T> v = *this;
		v.normalize();
		return v;
	}
	template<typename T>
	void Vector2<T>::normalize()
	{
		float s = 1.0f / size();
		x *= s;
		y *= s;
	}
	template<typename T>
	T Vector2<T>::size() const
	{
		return sqrt(sizeSquare());
	}
	template<typename T>
	T Vector2<T>::sizeSquare() const
	{
		return x*x + y*y;
	}
	template<typename T>
	Vector2<T> Vector2<T>::lerp(float t, const Vector2& v) const
	{
		return Vector2(x * (1 - t) + v.x * t, y * (1 - t) + v.y * t);
	}

#if _IPHONE_
#pragma mark vector3 impl
#endif
	template<typename T>
	Vector3<T>::Vector3() : x(0), y(0), z(0) {}

	template<typename T>
	Vector3<T>::Vector3(T x, T y, T z) : x(x), y(y), z(z) {}

	template<typename T>
	Vector3<T>::Vector3(T *ptr) : x(ptr[0]), y(ptr[1]), z(ptr[2]) {}

	template<typename T>
	Vector3<T>::Vector3(const T *ptr) : x(ptr[0]), y(ptr[1]), z(ptr[2]) {}

	template<typename T>
	T* Vector3<T>::pointer() { return &x; }

	template<typename T>
	void Vector3<T>::normalize()
	{
		float s = 1.0f / std::sqrt(x * x + y * y + z * z);
		x *= s;
		y *= s;
		z *= s;
	}
	template<typename T>
	Vector3<T> Vector3<T>::normal() const
	{
		Vector3<T> v = *this;
		v.normalize();
		return v;
	}
	template<typename T>
	Vector3<T> Vector3<T>::cross(const Vector3& v) const
	{
		return Vector3(y * v.z - z * v.y,
					   z * v.x - x * v.z,
					   x * v.y - y * v.x);
	}
	template<typename T>
	T Vector3<T>::dot(const Vector3& v) const
	{
		return x * v.x + y * v.y + z * v.z;
	}
	template<typename T>
	Vector3<T> Vector3<T>::operator+(const Vector3& v) const
	{
		return Vector3(x + v.x, y + v.y,  z + v.z);
	}
	template<typename T>
	void Vector3<T>::operator+=(const Vector3& v)
	{
		x += v.x;
		y += v.y;
		z += v.z;
	}
	template<typename T>
	void Vector3<T>::operator-=(const Vector3& v)
	{
		x -= v.x;
		y -= v.y;
		z -= v.z;
	}
	template<typename T>
	void Vector3<T>::operator/=(T s)
	{
		x /= s;
		y /= s;
		z /= s;
	}
	template<typename T>
	Vector3<T> Vector3<T>::operator-(const Vector3& v) const
	{
		return Vector3(x - v.x, y - v.y,  z - v.z);
	}
	template<typename T>
	Vector3<T> Vector3<T>::operator-() const
	{
		return Vector3(-x, -y, -z);
	}
	template<typename T>
	Vector3<T> Vector3<T>::operator*(T s) const
	{
		return Vector3(x * s, y * s, z * s);
	}
	template<typename T>
	Vector3<T> Vector3<T>::operator/(T s) const
	{
		return Vector3(x / s, y / s, z / s);
	}
	template<typename T>
	bool Vector3<T>::operator==(const Vector3& v) const
	{
		return x == v.x && y == v.y && z == v.z;
	}
	template<typename T>
	Vector3<T> Vector3<T>::lerp(float t, const Vector3& v) const
	{
		return Vector3(x * (1 - t) + v.x * t,
					   y * (1 - t) + v.y * t,
					   z * (1 - t) + v.z * t);
	}

#if _IPHONE_
#pragma mark vector4 impl
#endif
	template<typename T>
	Vector4<T>::Vector4() {}

	template<typename T>
	Vector4<T>::Vector4(Vector3<T> v) : x(v.x), y(v.y), z(v.z), w(1) {}

	template<typename T>
	Vector4<T>::Vector4(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) {}

	template<typename T>
	Vector4<T>::Vector4(T* ptr) : x(ptr[0]), y(ptr[1]), z(ptr[2]), w(ptr[3]) {}

	template<typename T>
	Vector4<T>::Vector4(const T* ptr) : x(ptr[0]), y(ptr[1]), z(ptr[2]), w(ptr[3]) {}

	template<typename T>
	T Vector4<T>::dot(const Vector4& v) const
	{
		return x * v.x + y * v.y + z * v.z + w * v.w;
	}

	template<typename T>
	Vector4<T>::Vector4(const Vector3<T> &v, T w)
	: w(w)
	{
		x = v.x;
		y = v.y;
		z = v.z;
	}

	template<typename T>
	Vector4<T> Vector4<T>::lerp(float t, const Vector4& v) const
	{
		return Vector4(x * (1 - t) + v.x * t,
					   y * (1 - t) + v.y * t,
					   z * (1 - t) + v.z * t,
					   w * (1 - t) + v.w * t);
	}

	template<typename T>
	T* Vector4<T>::pointer() { return &x;	}

	template<typename T>
	Vector3<T> Vector4<T>::toVec3() const
	{
		return Vector3<T>(x, y, z);
	}
	template<typename T>
	Vector2<T> Vector4<T>::toVec2() const
	{
		return Vector2<T>(x, y);
	}
	template<typename T>
	bool Vector4<T>::operator==(const Vector4& v) const
	{
		if(x == v.x && y == v.y && z == v.z && w == v.w)
			return true;
		else
			return false;
	}
	template<typename T>
	bool Vector4<T>::operator!=(const Vector4& v) const
	{
		return !(*this == v);
	}
}
